package com.szjz.freequery;

import com.szjz.freequery.util.FQUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * author:史博
 * date:2019/7/9
 * <p>
 * 用于查询对象中的任意列
 */
@Data
@Slf4j
@Component
public class FQObj {

//    @Autowired
//    private FQProperties fqProperties;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private List<String> conditionList = new ArrayList<>();

    private Object object;

    private String tableName;

    /**
     * where 后面的条件语句
     */
    public FQObj addCondition(String condition) {
        conditionList.add(condition.replaceAll(" +", ""));
        if (conditionList.contains("id=null")) {
            //如果包含非法字符 清空数组 防止缓存
            this.conditionList = new ArrayList<>();
            throw new RuntimeException("请输入id");
        }
        return this;
    }

    /**
     * 添加实体类
     */
    public FQObj addObject(Object object) {
        this.object = object;
        return this;
    }

    /**
     * 获取执行查询之后的数据
     */
    public Object query() {
        return getData(object);
    }

    /**
     * 在Controller中直接调用此方法即可
     *
     * @param obj 实体类
     * @return 根据需求查询的列
     */
    private Object getData(Object obj) {
        List<Map<String, Object>> mapList = query(obj);
        if (mapList != null) {
            if (mapList.size() == 0) {
                return "数据不存在";
            }
            return mapList.get(0);
        }
        return null;
    }

    /**
     * 列表查询
     */
    private List<Map<String, Object>> query(Object obj) {

        Class clazz = obj.getClass();
        //获取列名集合
        List<String> fieldNames = FQUtil.getFieldNames(obj);
        //创建sql
        String sql = createSql(fieldNames, clazz, obj);
        //执行查询
        if (sql != null) {
            List<Map<String, Object>> mapList = FQUtil.queryCol(sql, jdbcTemplate);
            return mapList;
        }
        return null;
    }


    /**
     * 组装sql语句
     */
    private String createSql(List<String> fieldNames, Class clazz, Object obj) {
        //开始组装sql语句
        StringBuffer sql = new StringBuffer("SELECT ");
        //如果有标记列就查询对应的列
        if (fieldNames.size() > 0 && fieldNames != null) {

            //判断是否有主键
            if (fieldNames.size() == 1) {
                List<Field> allFields = new ArrayList<>();
                List<Field> fields = Arrays.asList(clazz.getDeclaredFields());
                //子类是否存在主键
                Boolean isExistId = isExistPrimaryKey(fieldNames, fields);
                //父类是否存在主键
                Boolean superClassIsExistId = false;
                if (clazz.getSuperclass() != null) {
                    List<Field> superFields = Arrays.asList(clazz.getSuperclass().getDeclaredFields());
                    superClassIsExistId = isExistPrimaryKey(fieldNames, superFields);
                }

                //存在主键
                if (isExistId || superClassIsExistId) {
                    List<String> queriedFieldList = new ArrayList<>();

                    allFields = FQUtil.getValidFields(fields, allFields);
                    allFields = FQUtil.addSuperFields(clazz, allFields);

                    for (Field field : allFields) {
                        String underline = FQUtil.humpToUnderlineNaming(field.getName());
                        queriedFieldList.add(underline);
                    }
                    sql.append(StringUtils.join(queriedFieldList, ","));
                }
                //不存在主键 无法查询
                else {
//                    if (fqProperties.getLog().getLevel().equals(LogLevelModelEnum.debug)) {
                    log.error("【FQ】不存在主键");
//                    }
                    throw new RuntimeException("不存在主键");
                }
            }
            //包含主键和其他非隐藏字段
            else {
                List<String> queriedFieldList = new ArrayList<>();
                for (String fieldName : fieldNames) {
                    String underline = FQUtil.humpToUnderlineNaming(fieldName);
                    queriedFieldList.add(underline);
                }
                sql.append(StringUtils.join(queriedFieldList, ","));
            }
            sql.append(" FROM ");
            //添加表名(默认格式：***_***)
            String tableName = FQUtil.humpToUnderlineNaming(clazz.getSimpleName());
            sql.append(this.tableName == null ? tableName : this.tableName);

            //添加where条件
            if (conditionList.size() > 0 && conditionList != null) {
                sql.append(" WHERE ");
                sql.append(StringUtils.join(conditionList, " AND "));
            }

//            if (fqProperties.getLog().getLevel().equals(LogLevelModelEnum.debug)) {
            log.debug("【FQ】query SQL : {}", sql);
//            }
            conditionList = new ArrayList<>();
            return new String(sql);
        } else {
            return null;
        }

    }

    /**
     * fields中是否存在主键注解@Id 且fieldNames中是否存在主键字段
     *
     * @param fieldNames 欲查询的字段名称集合
     * @param fields     字段集合
     * @return
     */
    private Boolean isExistPrimaryKey(List<String> fieldNames, List<Field> fields) {
        Boolean isExistId = false;
        for (Field superField : fields) {
            List<Annotation> annotations = Arrays.asList(superField.getAnnotations());
            if (annotations != null && annotations.size() > 0) {
                //获取主键（被@Id标注的为主键）
                for (Annotation annotation : annotations) {
                    if (annotation.annotationType().getSimpleName().equals("Id")) {
                        if (fieldNames.get(0).equals(superField.getName())) {
                            isExistId = true;
                            break;
                        }
                    }
                }
            }
        }
        log.debug("【FQ】primary key is exist : {}", isExistId);
        return isExistId;
    }

}
